def sigmoid(x):
    return 1/(1+np.exp(-x))

def dsigmoid(x):
    return x*(1-x)

class NeuralNetwork:
    def __init__(self,layers):  # (64,100,50,10)
        # 权值的初始化，范围-1到1
        self.U = np.random.random((layers[0]+1,layers[1]+1))*2-1
        self.V = np.random.random((layers[1]+1,layers[2]+1))*2-1
        self.W = np.random.random((layers[2]+1,layers[3]))*2-1
        
    def train(self,X,y,X_test,y_test,lr=0.11,epochs=10000):
        # 添加偏置
        temp = np.ones([X.shape[0],X.shape[1]+1])
        temp[:,0:-1] = X  # 最后一列都是1
        X = temp
        
        for n in range(epochs+1):
            i = np.random.randint(X.shape[0]) # 随机选取一个数据
            x = [X[i]]
            x = np.atleast_2d(x)  # 转为2维数据 (1, 65)

            L0 = sigmoid(np.dot(x,self.U))
            L1 = sigmoid(np.dot(L0,self.V))  # 隐层输出
            L2 = sigmoid(np.dot(L1,self.W))  # 输出层输出
            
            L2_delta = (y[i]-L2)*dsigmoid(L2)
            L1_delta= L2_delta.dot(self.W.T)*dsigmoid(L1)
            L0_delta= L1_delta.dot(self.V.T)*dsigmoid(L0)
            
            self.W += lr*L1.T.dot(L2_delta)
            self.V += lr*L0.T.dot(L1_delta)
            self.U += lr*x.T.dot(L0_delta)
            
            #每训练1000次预测一次准确率
            if n%1000==0:
                predictions = []
                for j in range(X_test.shape[0]):
                    o = self.predict(X_test[j])
                    predictions.append(np.argmax(o)) # 获取预测结果
                self.accuracy = np.mean(np.equal(predictions,y_test))
                print('epoch:',n,'accuracy:',self.accuracy)
        
    def predict(self,x):
        #添加偏置
        temp = np.ones(x.shape[0]+1)
        temp[0:-1] = x
        x = temp
        x = np.atleast_2d(x) # 转为2维数据
        
        L0 = sigmoid(np.dot(x,self.U))
        L1 = sigmoid(np.dot(L0,self.V))  # 隐层输出
        L2 = sigmoid(np.dot(L1,self.W))  # 输出层输出
        
        return L2
